package com.sunteam.flutter_oss_plugin

import android.content.Context
import android.util.Log
import androidx.annotation.NonNull
import com.alibaba.sdk.android.oss.ClientException
import com.alibaba.sdk.android.oss.OSS
import com.alibaba.sdk.android.oss.OSSClient
import com.alibaba.sdk.android.oss.ServiceException
import com.alibaba.sdk.android.oss.callback.OSSCompletedCallback
import com.alibaba.sdk.android.oss.callback.OSSProgressCallback
import com.alibaba.sdk.android.oss.common.auth.OSSCustomSignerCredentialProvider
import com.alibaba.sdk.android.oss.internal.OSSAsyncTask
import com.alibaba.sdk.android.oss.model.PutObjectRequest
import com.alibaba.sdk.android.oss.model.PutObjectResult
import io.flutter.plugin.common.MethodCall
import io.flutter.plugin.common.MethodChannel
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.GlobalScope
import kotlinx.coroutines.launch

class CustomOssManager constructor(var channel: MethodChannel) {
    var oss: OSS? = null
    private var ossAsyncTaskMap: HashMap<String, OSSAsyncTask<PutObjectResult>> = hashMapOf()

    fun checkMethod(
        @NonNull call: MethodCall,
        @NonNull result: MethodChannel.Result,
        context: Context
    ): Boolean {
        var check = false
        when (call.method) {
            "initCustomOss" -> {
                buildOss(
                    endPoint = Util.getCallArgument(call, "endPoint") ?: "",
                    stsServer = Util.getCallArgument(call, "stsServer") ?: "",
                    userAgent = Util.getCallArgument(call, "userAgent") ?: "",
                    needLog = Util.getCallArgument<Boolean>(call, "needLog") ?: false,
                    needDns = Util.getCallArgument<Boolean>(call, "needDns") ?: false,
                    maxConcurrentNumber = Util.getCallArgument<Int>(call, "maxConcurrencyNum") ?: 5,
                    maxRetryNumber = Util.getCallArgument<Int>(call, "retryNum") ?: 2,
                    connectTimeOut = Util.getCallArgument<Int>(call, "connectOutTime") ?: 15 * 1000,
                    socketTimeOut = Util.getCallArgument<Int>(call, "socketOutTime") ?: 15 * 1000,
                    ctx = context,
                    sign = Util.getCallArgument(call, "sign") ?: ""
                )
                check = true
            }
            "customOssUpload" -> {
                Log.e("OssManager", "uploadFile")
                var backTag = Util.getCallArgument<String>(call, "backTag")!!
                var bucketName = Util.getCallArgument<String>(call, "bucketName")

                if (bucketName == null || bucketName.isEmpty()) {
                    result.error("-1", "bucketName isEmpty", "please set parame bucketName")
                    return true
                }
                var objectName = Util.getCallArgument<String>(call, "objectName")

                val ossAsyncTask = uploadFileByPath(
                    bucketName,
                    objectName = objectName
                        ?: "",
                    filePath = Util.getCallArgument<String>(call, "filePath")
                        ?: "",
                    progressCallback = OSSProgressCallback<PutObjectRequest> { request, currentSize, totalSize ->
                        GlobalScope.launch(Dispatchers.Main) {
                            var progress = (currentSize * 100.0 / totalSize)
                            channel.invokeMethod(
                                "progressCallBack", mapOf(
                                    "tag" to backTag,
                                    "progress" to progress
                                )
                            )
                        }

                    },
                    resultCallBack = object :
                        OSSCompletedCallback<PutObjectRequest, PutObjectResult> {
                        override fun onSuccess(
                            request: PutObjectRequest?,
                            ossResult: PutObjectResult?
                        ) {
                            if (!ossAsyncTaskMap.containsKey(backTag)) {
                                result.error("-1", "Task not found", "Task canceled")
                                return
                            }
                            var serverJson = getFileOssPath(
                                bucketName,
                                objectName ?: "",
                                Util.getCallArgument<Boolean>(call, "isSign") ?: false
                            )
                            GlobalScope.launch(Dispatchers.Main) {
                                channel.invokeMethod(
                                    "completeCallBack",
                                    mapOf("tag" to backTag, "requestId" to serverJson)
                                )
                                result.success(serverJson)
                            }

                        }

                        override fun onFailure(
                            request: PutObjectRequest?,
                            clientException: ClientException?,
                            serviceException: ServiceException?
                        ) {
                            if (clientException != null) {
                                GlobalScope.launch(Dispatchers.Main) {
                                    channel.invokeMethod(
                                        "clientError",
                                        mapOf(
                                            "tag" to backTag,
                                            "errorMessage" to clientException.message
                                        )
                                    )
                                }

                            } else if (serviceException != null) {
                                GlobalScope.launch(Dispatchers.Main) {
                                    channel.invokeMethod(
                                        "serverError", mapOf(
                                            "tag" to backTag,
                                            "errorMessage" to serviceException.rawMessage,
                                            "code" to serviceException.errorCode,
                                            "hostId" to serviceException.hostId
                                        )
                                    )
                                }

                            }
                        }
                    })

                ossAsyncTask?.let { ossAsyncTaskMap.put(backTag, it) }
                check = true
            }
            "customCancelTask" -> {
                val backTag = Util.getCallArgument<String>(call, "backTag")!!
                val ossAsyncTask: OSSAsyncTask<PutObjectResult>? = ossAsyncTaskMap[backTag]

                val mapResult = excuteCancel(backTag, ossAsyncTask)
                ossAsyncTaskMap.remove(backTag)
                GlobalScope.launch(Dispatchers.Main) {
                    channel.invokeMethod("cancelTask", mapOf("tasks" to arrayListOf(mapResult)))
                }
            }
            "customCancelAllTask" -> {
                Log.e("OssManager", "cancelAllTask :$ossAsyncTaskMap")
                val tasks = arrayListOf<Map<String, Any>>()
                ossAsyncTaskMap.forEach {
                    val mapResult = excuteCancel(it.key, it.value)
                    Log.e("OssManager", "cancelAllTask $mapResult")
                    tasks.add(mapResult)
                }
                ossAsyncTaskMap.clear()
                GlobalScope.launch(Dispatchers.Main) {
                    channel.invokeMethod("cancelTask", mapOf("tasks" to tasks))
                }
            }
        }
        return check
    }

    fun buildOss(
        endPoint: String,
        stsServer: String,
        userAgent: String,
        needLog: Boolean,
        needDns: Boolean,
        connectTimeOut: Int,
        socketTimeOut: Int,
        maxConcurrentNumber: Int,
        maxRetryNumber: Int,
        ctx: Context,
        sign: String
    ) {
        var conf = ConfigManager.buildConfig(
            userAgent, needLog, needDns, connectTimeOut,
            socketTimeOut, maxConcurrentNumber, maxRetryNumber
        )
        var provider = object : OSSCustomSignerCredentialProvider() {
            override fun signContent(content: String?): String {
                return sign
            }
        }
        oss = OSSClient(ctx, endPoint, provider, conf)
    }

    fun uploadFileByPath(
        bucketName: String,
        objectName: String,
        filePath: String,
        resultCallBack: OSSCompletedCallback<PutObjectRequest, PutObjectResult>,
        progressCallback: OSSProgressCallback<PutObjectRequest>
    ): OSSAsyncTask<PutObjectResult>? {
        return FileManager.uploadFileByPath(
            oss,
            bucketName,
            objectName,
            filePath,
            resultCallBack,
            progressCallback
        )
    }

    fun getFileOssPath(bucketName: String, objectName: String, isSign: Boolean): String {
        return FileManager.getFileOssPath(oss, bucketName, objectName, isSign)
    }

    private fun excuteCancel(
        tag: String,
        ossAsyncTask: OSSAsyncTask<PutObjectResult>?
    ): Map<String, Any> {
        var code = 0
        var msg = "结束任务失败"
        if (ossAsyncTask?.isCompleted!!) {
            code = 2
            msg = "任务已完成无法结束"
        } else if (!ossAsyncTask?.isCanceled!!) {
            code = 1
            msg = "任务已取消"
            ossAsyncTask?.cancel()
        }
        return mapOf("tag" to tag, "code" to code, "msg" to msg)
    }

}